1. Basic Java Syntax & Useful Built-in Methods

该笔记记录的内容并不全面!

由于作者在开始本节课的学习之前已经对 Java 基本语法有一些基本的了解,该部分不会全面而详尽的叙述相关的知识,只是会简要列出一些容易混淆或忘记的知识点。

详尽的叙述可以参考 CS61B 电子课本Java 教程

类的成员分为两种,静态成员(Static Member)实例成员(Instance Member),后者只能被类的实例访问;而静态成员既可以通过类名访问也可以通过实例访问,它们会访问到 同一个 对象上。然而,从代码风格的角度讲,在绝大多数情况下静态成员 都应该通过类名访问,通过实例访问静态成员是一个不好的写法。

Java 中,除了八种基本的数据类型之外,其余所有数据类型都是 引用类型(Reference Type)。每一个被声明为引用类型的变量本质上都只是一个存储着 指向该对象实例在内存中实际存储地址的指针,大小为机器字长。这些指针初始值均为 0x0,即 null

Java 中函数只有按值传参这一种传参方式。然而,当我们传入一些指向引用类型对象实例的变量时,也能隐式的做到按引用传参。

嵌套类

Java 中的类可以嵌套。嵌套的类可以通过 static 被声明为静态类。嵌套的静态类只能访问 外围类(Enclosing Class) 中的静态属性。然而,顶级类不能被声明为静态类。

高阶函数

在 Java 7 及之前,由于不允许变量存储指向某个函数的引用,在 Java 中实现高阶函数的写法相当丑陋,需要借助接口间接实现。然而,Java 8 提供了函数式接口与 Lambda 表达式特性,使得书写高阶函数简单了很多。

函数式接口是指有且仅有一个抽象方法的接口。Java 8 提供了 java.util.function 包,其中内置了函数式接口,例如 Function<T, R>Consumer<T>Predicate<T> 等。这些接口与实现了这些接口的类可以直接作为参数或返回值使用。为自己书写的接口添加 @FunctionalInterface 标注可以将该接口声明为自定义的函数式接口。

常用的内置函数式接口的用法如下:

由于函数式接口被强制规定了只能有一个抽象方法,Java 中的 Lambda 表达式与函数式接口之间可以进行隐式的转换,即定义的 Lambda 表达式将自动被视为接口所声明方法的实现。Lambda 表达式的格式如下:

(<parameters>) -> {<suite>};

当参数只有一个时,括号可以省略。参数的类型无需显示声明,此时编译器依据其被赋值给的函数式接口自行推断参数类型。

与 Python 不同而与 C++ 类似,Java 中的 Lambda 表达式的函数体可以包含多行。

Lambda 函数是 懒执行 的,具体解释可以见11. 迭代器与生成器 Iterators & Generators

Java 允许通过 类::方法 的语法传递方法引用,这种写法广泛用于 Java 8 引入的另一特性 stream 中,如在 mapfilterreduce 等函数(它们的功能与 Python 中的同名函数类似)中。

Comparable 与 Comparator

Java 有两个内置的泛型接口 Comparable<T>Comparator<T>,允许为自定义类创建自定义的比较方法。二者均为函数式接口,各自定义了 public int compareTo(T obj)public int compare(T o1, T o2) 方法。

Java 出于如下目的区分出这两种比较的方法:

所谓的“大”、“小”与“相等”

假设我们需要对一个包含某个类对象的数列排序,Java 会依照实现的 compareTo 方法返回的结果将对象由“小”到“大”进行排序。所谓的“小”、“大”与“相等”只由比较方法的返回的结果确定。

因此,我们如何实现 compareTo 方法决定了我们如何看待什么样的类实例是更“大”的。一般而言,实现 compareTo 方法时,需要尽量贴近现实生活中的常识,而将各种自定义比较方法通过 Comparator 实现。

一般而言,当当对象的排序逻辑是唯一的、自然的(如数值大小、字典序),且可以修改类代码时,使用 Comparable;否则当我们需要多种排序方式(如按年龄、姓名、薪资等不同维度);无法修改类的源码(如第三方库的类);或需要动态组合排序规则(如先按年龄,再按姓名)时,使用 Comparator

Array & Set

Java 内置了 ListSet 接口。二者均继承自集合接口 Collections,分别规定了列表与集合两种数据结构。两个接口都规定了大量方法,具体可见 Java ListJava Set,此处不再赘述。Java 内置了实现了 List 接口的 LinkedList 类与 ArrayList 类与实现了 Set 接口的 HashSet 类与 TreeSet 类。

Iterator

与 Python 类似,Java 中同样存在迭代器,其遍历某个可迭代对象中的所有元素一次且仅一次。实现了 Iterable<T> 接口的类可以通过调用 iterator() 方法得到某个可迭代实例的迭代器,并可以使用 for (object : iterable) 循环遍历内部所有元素。

除了实现 iterator() 方法外,为了使返回的迭代器可以正常工作,还需要实现 Iterator<T> 接口的 hasNext()next() 方法。

事实上,IterableCollection 的父类,而后者是 ListSet 等容器接口的父类。
关于使用迭代器时可能发生的未定义行为

在调用某个迭代器的 hasNext() 方法并返回 False 后继续调用 next() 方法属于 未定义行为。然而,绝大多数 Iterator::hasNext 方法的实现都会在这种情况抛出 NoSuchElementException 异常,在编写自定义迭代器时也应遵循这一点。

Object

Java 中的所有类都继承自 Object 类,无论是否显式指定继承。Object 类中的所有方法详见 Java Object。这里仅就几个常用的方法做说明: